package com.guodefu.demo.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.guodefu.demo.config.CustomCacheable;
import com.guodefu.demo.domain.DictionaryDO;
import com.guodefu.demo.dto.DictionaryDTO;
import com.guodefu.demo.mapper.DictionaryMapper;
import com.guodefu.demo.mapstruct.DictionaryMapStructMapper;
import com.guodefu.demo.service.DictionaryService;
import com.guodefu.unifiedresponse.ResultEnum;
import jakarta.annotation.Nonnull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class DictionaryServiceImpl implements DictionaryService {

    private final DictionaryMapStructMapper mapStructMapper;

    private final DictionaryMapper dictionaryMapper;

    private final RedisTemplate<String, DictionaryDO> redisTemplate;

    private static final String DICTIONARY_CACHE_NAME = "dictionaryCache";
    private static final String CACHE_PREFIX = DICTIONARY_CACHE_NAME + ":";

    @Autowired
    public DictionaryServiceImpl(DictionaryMapStructMapper mapStructMapper, DictionaryMapper dictionaryMapper,
                                 RedisTemplate<String, DictionaryDO> redisTemplate) {
        this.dictionaryMapper = dictionaryMapper;
        this.redisTemplate = redisTemplate;
        this.mapStructMapper = mapStructMapper;
    }

    @Override
    @Cacheable(value = DICTIONARY_CACHE_NAME, key = "#id")
    public DictionaryDO getOneById(Long id) {
        DictionaryDO dictionaryDO = dictionaryMapper.selectById(id);
        ResultEnum.NO_SUCH_ELEMENT.assertNotNull(dictionaryDO);
        return dictionaryDO;
    }

    @Override
    @Cacheable(value = DICTIONARY_CACHE_NAME, key = "#type + ':' + #code")
    public DictionaryDO getOneByTypeAndCode(String type, String code) {
        return dictionaryMapper.selectOne(
                new QueryWrapper<DictionaryDO>().eq("type", type).eq("code", code)
        );
    }

    @Override
    @Caching(put={
            @CachePut(value = DICTIONARY_CACHE_NAME, key = "#dict.type + ':' + #dict.code"),
            @CachePut(value = DICTIONARY_CACHE_NAME, key = "#result.id")
    })
    public DictionaryDO insert(DictionaryDTO dict) {
        DictionaryDO dictionaryDO = mapStructMapper.toDO(dict);
        dictionaryMapper.insertOrUpdate(dictionaryDO);
        // 再查一遍是因为mybatis只会回写id，而createTime和updateTime不会回写
        return dictionaryMapper.selectById(dictionaryDO.getId());
    }

    @Override
    @Caching(put={
            @CachePut(value = DICTIONARY_CACHE_NAME, key = "#dict.type + ':' + #dict.code"),
            @CachePut(value = DICTIONARY_CACHE_NAME, key = "#result.id")
    })
    public DictionaryDO update(Long id, DictionaryDTO dict) {
        DictionaryDO dictionaryDO = mapStructMapper.toDO(dict);
        dictionaryDO.setId(id);
        dictionaryMapper.insertOrUpdate(dictionaryDO);
        // 再查一遍是因为mybatis只会回写id，而createTime和updateTime不会回写
        return dictionaryMapper.selectById(id);
    }

    /**
     * id,type,code 必传
     */
    @Caching(evict={
            @CacheEvict(value = DICTIONARY_CACHE_NAME, key = "#dict.type + ':' + #dict.code"),
            @CacheEvict(value = DICTIONARY_CACHE_NAME, key = "#dict.id")
    })
    public int delete(DictionaryDO dict) {
        if (StringUtils.hasText(dict.getType()) && StringUtils.hasText(dict.getCode())) {
            return dictionaryMapper.deleteById(dict);
        }
        return 0;
    }

    /**
     * 根据类型获取缓存的字典数据
     *
     * @param type Dictionary type
     * @return List of Dictionary objects of the specified type
     */
    @Override
    public List<DictionaryDO> getListByType(String type) {
        // 匹配所有以 "dictionaryCache:type:" 开头的键
        String pattern = CACHE_PREFIX + type + ":*";
        return getDictionaryDOList(pattern);
    }

    /**
     * 根据名称或类型获取缓存的字典数据
     *
     * @param name Dictionary name
     * @param type Dictionary type
     * @return List of Dictionary objects that match the name and/or type
     */
    @Override
    public List<DictionaryDO> getByNameOrType(@Nonnull String name, String type) {
        // 获取所有以 "dictionaryCache:type:" 开头的键，如果类型为空，则获取所有
        String pattern = StringUtils.hasText(type) ? CACHE_PREFIX + type + ":*" : CACHE_PREFIX + "*";
        List<DictionaryDO> dictionaries = getDictionaryDOList(pattern);
        return dictionaries.stream()
                .filter(dict -> (dict.getName().contains(name)))
                .collect(Collectors.toList());
    }

    /**
     * 没有使用缓存，直接查的数据库，用于启动时的缓存初始化
     */
    @Override
    public List<DictionaryDO> list() {
        return dictionaryMapper.selectList(null);
    }

    /**
     * 用于启动时的缓存初始化
     */
    @Override
    @CustomCacheable(value = DICTIONARY_CACHE_NAME, key = "#dict.type + ':' + #dict.code")
    @CachePut(value = DICTIONARY_CACHE_NAME, key = "#dict.type + ':' + #dict.code")
    public DictionaryDO cacheByTypeAndCode(DictionaryDO dict) {
        return dict;
    }

    /**
     * 用于启动时的缓存初始化
     */
    @Override
    @CachePut(value = DICTIONARY_CACHE_NAME, key = "#dict.id")
    public DictionaryDO cacheById(DictionaryDO dict) {
        return dict;
    }


    private List<DictionaryDO> getDictionaryDOList(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys == null || keys.isEmpty()) {
            return Collections.emptyList();
        }
        List<DictionaryDO> dictionaries = redisTemplate.opsForValue().multiGet(keys);
        if (dictionaries == null) {
            return Collections.emptyList();
        }
        dictionaries.sort(Comparator.comparing(DictionaryDO::getId)); // 按年龄升序排序
        return dictionaries;
    }

}
